﻿//PlazaType.cs
//Copyright (c) 2013 StockSharp LLC, all rights reserved.
//This code module is part of StockSharp library.
//This code is licensed under the GNU GENERAL PUBLIC LICENSE Version 3.
//See the file License.txt for the license details.
//More info on: http://stocksharp.com

namespace StockSharp.Plaza.Metadata
{
	using System;

	using Ecng.Common;

	/// <summary>
	/// Тип данных протокола Plaza.
	/// </summary>
	public class PlazaType
	{
		internal PlazaType(Type dotNetType)
		{
			if (dotNetType == null)
				throw new ArgumentNullException("dotNetType");

			DotNetType = dotNetType;
		}

		/// <summary>
		/// .NET тип данных, соответствующий Plaza.
		/// </summary>
		public Type DotNetType { get; private set; }

		/// <summary>
		/// Привести в строковое представление.
		/// </summary>
		/// <returns>Строковое представление.</returns>
		public override string ToString()
		{
			// В документации также указаны беззнаковые целые (u2, u4, u8), но полей такого типа не существует.
			if (DotNetType == typeof(bool))
				return "i1";
			else if (DotNetType == typeof(short))
				return "i2";
			else if (DotNetType == typeof(int))
				return "i4";
			else if (DotNetType == typeof(long))
				return "i8";
			else if (DotNetType == typeof(DateTime))
				return "t";
			else if (DotNetType == typeof(float) || DotNetType == typeof(double))
				return "f";
			else
				throw new InvalidOperationException("Неприемлемый тип данных: {0}".Put(DotNetType.FullName));
		}

		/// <summary>
		/// Привести <see cref="Type"/> значение к объекту <see cref="PlazaType"/>.
		/// </summary>
		/// <param name="type">.NET тип данных соответствующий Plaza.</param>
		/// <returns>Объект <see cref="PlazaType"/>.</returns>
		public static implicit operator PlazaType(Type type)
		{
			return new PlazaType(type);
		}

		/// <summary>
		/// Привести <see cref="string"/> значение к объекту <see cref="PlazaType"/>.
		/// </summary>
		/// <param name="value">Строка, описывающая тип Plaza.</param>
		/// <returns>Объект <see cref="PlazaType"/>.</returns>
		public static implicit operator PlazaType(string value)
		{
			return Parse(value);
		}

		/// <summary>
		/// Перевести строку, описывающая тип Plaza (например, d116.5), в объект <see cref="PlazaType"/>.
		/// </summary>
		/// <param name="value">Строка, описывающая тип Plaza.</param>
		/// <returns>Объект <see cref="PlazaType"/>.</returns>
		public static PlazaType Parse(string value)
		{
			if (value.IsEmpty())
				throw new ArgumentNullException("value");

			value = value.ToLowerInvariant();

			switch (value)
			{
				case "i1":
					return new PlazaType(typeof(bool));
				case "i2":
					return new PlazaType(typeof(short));
				case "i4":
					return new PlazaType(typeof(int));
				case "i8":
					return new PlazaType(typeof(long));
				case "u1":
					return new PlazaType(typeof(bool));
				case "u2":
					return new PlazaType(typeof(ushort));
				case "u4":
					return new PlazaType(typeof(uint));
				case "u8":
					return new PlazaType(typeof(ulong));
				case "t":
					return new PlazaType(typeof(DateTime));
				case "f":
					return new PlazaType(typeof(double));
				default:
				{
					try
					{
						if (value.StartsWith("c"))
						{
							return new PlazaStringType(value.Substring(1).To<int>());
						}
						else if (value.StartsWith("d"))
						{
							var parts = value.Substring(1).Split(new[] { '.' });
							return new PlazaDecimalType(parts[0].To<int>(), parts[1].To<int>());
						}
					}
					catch (Exception ex)
					{
						throw new ArgumentException("Неверный формат: {0}".Put(value), ex);
					}
					
					throw new ArgumentException("Неверный формат: {0}".Put(value));
				}
			}
		}
	}

	/// <summary>
	/// Тип строковых данных протокола Plaza.
	/// </summary>
	public class PlazaStringType : PlazaType
	{
		internal PlazaStringType(int length)
			: base(typeof(string))
		{
			if (length < 1)
				throw new ArgumentOutOfRangeException("length");

			Length = length;
		}

		/// <summary>
		/// Длина строки.
		/// </summary>
		public int Length { get; private set; }

		/// <summary>
		/// Привести в строковое представление.
		/// </summary>
		/// <returns>Строковое представление.</returns>
		public override string ToString()
		{
			return "c{0}".Put(Length);
		}
	}

	/// <summary>
	/// Тип десятичных данных протокола Plaza.
	/// </summary>
	public class PlazaDecimalType : PlazaType
	{
		internal PlazaDecimalType(int precision, int scale)
			: base(typeof(decimal))
		{
			if (precision < 1)
				throw new ArgumentOutOfRangeException("precision");

			if (scale < 1)
				throw new ArgumentOutOfRangeException("scale");

			Precision = precision;
			Scale = scale;
		}

		/// <summary>
		/// Количество знаков до запятой.
		/// </summary>
		public int Precision { get; private set; }

		/// <summary>
		/// Количество знаков после запятой.
		/// </summary>
		public int Scale { get; private set; }

		/// <summary>
		/// Привести в строковое представление.
		/// </summary>
		/// <returns>Строковое представление.</returns>
		public override string ToString()
		{
			return "d{0}.{1}".Put(Precision, Scale);
		}
	}

	///// <summary>
	///// Этот класс используется, когда автоматическая конвертация класса PlazaType не подходит
	///// Например, в таблице orders_log есть колонка action (i1), которую нужно конвертировать в int (у нее три возможных значения: 0, 1, 2)
	///// </summary>
	//public class PlazaActionType : PlazaType
	//{
	//    internal PlazaActionType() 
	//        : base(typeof(int))
	//    {
	//    }
	//}
}